﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Services.Client;
using System.Linq;
using BusinessObjects;
using YourWorld.Common.GreenWorldEntitiesService;
using System.Diagnostics;

namespace MainGameModule.Models
{
    public class PlayersRepository
    {
        public event Action<ObservableCollection<Player>> PlayersLoaded;

        private const string ServiceUrl = @"http://48de1d712dd7438f8e308ea3b1e69aae.cloudapp.net/GreenWorldDataService.svc";
        private GreenWorldEntities context;

        private static PlayersRepository instance;

        public static PlayersRepository Instance
        {
            get
            {
                if ( PlayersRepository.instance == null )
                {
                    PlayersRepository.instance = new PlayersRepository();
                }

                return PlayersRepository.instance;
            }
        }

        public DataServiceCollection<User> Users
        {
            get;
            private set;
        }

        public ObservableCollection<Player> Players
        {
            get;
            private set;
        }

        public PlayersRepository()
        {
            this.context = new GreenWorldEntities( new Uri( ServiceUrl, UriKind.RelativeOrAbsolute ) );
        }

        public Player GetPlayer( int id )
        {
            return this.Players.Where( x => x.Id == id ).FirstOrDefault();
        }

        public Player GetPlayerByUserId( int userId )
        {
            return this.Players.Where( x => x.UserId == userId ).FirstOrDefault();
        }

        public void Initialize()
        {
            this.LoadUsersAsync();
        }

        public void LoadUsersAsync()
        {
            var usersQuery = ( from r in this.context.Users select r ) as DataServiceQuery<User>;
            usersQuery.BeginExecute( new AsyncCallback( this.LoadUsersCompleted ), usersQuery );
        }

        public void Reload()
        {
            this.Users.Clear();
            this.Players.Clear();
            this.LoadUsersAsync();
        }

        private void LoadUsersCompleted( IAsyncResult state )
        {
            var query = from u in this.context.Users
                        select new User
                        {
                            Id = u.Id,
                            Email = u.Email,
                            Password = u.Password,
                            PicUrl = u.PicUrl,
                            Color = u.Color,
                            Name = u.Name,
                            Experience = u.Experience,
                            GameLevel = u.GameLevel,
                            Badges = u.Badges,
                            Quests = u.Quests,
                            Connections = u.Connections                           
                        };

            this.Users = new DataServiceCollection<User>();
            this.Users.LoadCompleted += new EventHandler<LoadCompletedEventArgs>( this.OnPlayersLoaded );
            this.Users.LoadAsync( query );
        }

        public void LoadPlayerProperties( Player player )
        {
            player.BadgesCollection = this.GetBadges( player.Badges );
            player.QuestsCollection = this.GetQuests( player.Quests );

            if ( player.IsGuest == true )
                return;

            User user = this.Users.Where( x => x.Id == player.UserId ).FirstOrDefault();
            if ( user != null )
            {
                player.PlayerConnections = this.GetConnections( user.Connections );
                player.Connections = this.Split( user.Connections );
            }
        }

        public void SavePlayerBadgeId( int playerId, int badgeId, AsyncCallback callback )
        {
            Player player = this.GetPlayer( playerId );
            if ( player.IsGuest == true )
            {
                if ( callback != null )
                    callback( null );

                return;
            }

            playerId = player.UserId;

            User editedUser = this.Users.Where( x => x.Id == playerId ).FirstOrDefault();
            Debug.Assert( editedUser != null, "There should be a user with Id - " + playerId );

            if ( String.IsNullOrEmpty( editedUser.Badges ) )
            {
                editedUser.Badges = badgeId.ToString();
            }
            else
            {
                editedUser.Badges = String.Format( "{0},{1}", editedUser.Badges, badgeId );
            }
            
            this.context.BeginSaveChanges( SaveChangesOptions.Batch,
                       ( result ) =>
                       {
                           if ( callback != null )
                               callback( null );
                       },
                       null );
        }

        public void SavePlayerConnectionId( int playerId, int otherPlayerId, AsyncCallback callback )
        {
            Player player = this.GetPlayer( playerId );
            if ( player.IsGuest == true )
            {
                if ( callback != null )
                    callback( null );

                return;
            }

            playerId = player.UserId;

            User editedUser = this.Users.Where( x => x.Id == playerId ).FirstOrDefault();
            Debug.Assert( editedUser != null, "There should be a user with Id - " + playerId );

            if ( String.IsNullOrEmpty( editedUser.Connections ) )
            {
                editedUser.Connections = otherPlayerId.ToString();
            }
            else
            {
                editedUser.Connections = String.Format( "{0},{1}", editedUser.Connections, otherPlayerId );
            }

            this.context.BeginSaveChanges( SaveChangesOptions.Batch,
                       ( result ) =>
                       {
                           if ( callback != null )
                               callback( null );
                       },
                       null );
        }

        public void SavePlayerQuestId( int playerId, int questId, AsyncCallback callback )
        {
            Player player = this.GetPlayer( playerId );
            if ( player.IsGuest == true )
            {
                if ( callback != null )
                    callback( null );

                return;
            }

            playerId = player.UserId;

            User editedUser = this.Users.Where( x => x.Id == playerId ).FirstOrDefault();
            Debug.Assert( editedUser != null, "There should be a user with Id - " + playerId );

            if ( String.IsNullOrEmpty( editedUser.Quests ) )
            {
                editedUser.Quests = questId.ToString();
            }
            else
            {
                editedUser.Quests = String.Format( "{0},{1}", editedUser.Quests, questId );
            }

            this.context.BeginSaveChanges( SaveChangesOptions.Batch,
                       ( result ) =>
                       {
                           if ( callback != null )
                               callback( null );
                       },
                       null );
        }

        public void SavePlayerExperience( int playerId, int experience, AsyncCallback callback )
        {
            Player player = this.GetPlayer( playerId );
            if ( player.IsGuest == true )
            {
                if ( callback != null )
                    callback( null );

                return;
            }

            playerId = player.UserId;

            User editedUser = this.Users.Where( x => x.Id == playerId ).FirstOrDefault();
            Debug.Assert( editedUser != null, "There should be a user with Id - " + playerId );

            editedUser.Experience = experience;

            this.context.BeginSaveChanges( SaveChangesOptions.Batch,
                       ( result ) =>
                       {
                           if ( callback != null )
                               callback( null );
                       },
                       null );
        }

        public void SavePlayerLevel( int playerId, int level, AsyncCallback callback )
        {
            Player player = this.GetPlayer( playerId );
            if ( player.IsGuest == true )
            {
                if ( callback != null )
                    callback( null );

                return;
            }

            playerId = player.UserId;

            User editedUser = this.Users.Where( x => x.Id == playerId ).FirstOrDefault();
            Debug.Assert( editedUser != null, "There should be a user with Id - " + playerId );

            editedUser.GameLevel = level;

            this.context.BeginSaveChanges( SaveChangesOptions.Batch,
                       ( result ) =>
                       {
                           if ( callback != null )
                               callback( null );
                       },
                       null );
        }

        private int GetExperience( int? experience )
        {
            if ( !experience.HasValue )
            {
                return 0;
            }
            else
            {
                return experience.Value;
            }
        }

        private int GetLevel( int? level )
        {
            if ( !level.HasValue )
            {
                return 0;
            }
            else
            {
                return level.Value;
            }
        }

        private ObservableCollection<UserBadge> GetBadges( string userBadges )
        {
            ObservableCollection<UserBadge> badgesCollection = new ObservableCollection<UserBadge>();

            foreach ( int badgeId in this.Split( userBadges ) )
                badgesCollection.Add( BadgesRepository.Instance.GetBadge( badgeId ) );

            return badgesCollection;
        }

        private ObservableCollection<Event> GetQuests( string userQuests )
        {
            ObservableCollection<Event> questsCollection = new ObservableCollection<Event>();

            foreach ( int questId in this.Split( userQuests ) )
                questsCollection.Add( RegionObjectsRepository.Instance.GetQuest( questId ) );

            return questsCollection;
        }

        private ObservableCollection<Player> GetConnections( string userConnections )
        {
            ObservableCollection<Player> connections = new ObservableCollection<Player>();

            foreach ( int playerId in this.Split( userConnections ) )
                connections.Add( PlayersRepository.Instance.GetPlayer( playerId ) );

            return connections;
        }

        private ObservableCollection<int> GetGameLevelList( int level )
        {
            ObservableCollection<int> levels = new ObservableCollection<int>();
            for ( int i = 0; i < level; ++i )
            {
                levels.Add( i );
            }
            return levels;
        }

        private ObservableCollection<int> Split( string ids )
        {
            ObservableCollection<int> resultIds = new ObservableCollection<int>();

            if ( String.IsNullOrEmpty( ids ) == false )
            {
                List<string> stringIds = ids.Split( ',' ).ToList();

                foreach ( string id in stringIds )
                {
                    int result = -1;
                    if ( Int32.TryParse( id, out result ) )
                    {
                        resultIds.Add( result );
                    }
                }
            }

            return resultIds;
        }

        private int GetMaxExperience()
        {
            if ( this.Players == null || this.Players.Count == 0 )
                return 0;

            return this.Players.Max( p => p.Experience );
        }

        private void OnPlayersLoaded( object sender, LoadCompletedEventArgs e )
        {
            this.Players = new ObservableCollection<Player>();
            foreach ( User user in this.Users )
            {
                Player player = new Player()
                {
                    // Id = user.Id,
                    UserId = user.Id,
                    Name = user.Name,
                    Experience = this.GetExperience( user.Experience ),
                    Level = this.GetLevel( user.GameLevel ),
                    Image = user.PicUrl,
                    BadgesCollection = this.GetBadges( user.Badges ),
                    QuestsCollection = this.GetQuests( user.Quests ),
                    PlayerConnections = this.GetConnections( user.Connections ),
                    GameLevelCollectionWorkaround = this.GetGameLevelList( this.GetLevel( user.GameLevel ) ),
                    Connections = this.Split( user.Connections )
                };
                this.Players.Add( player );
            }

            double maxExp = ( double )this.GetMaxExperience();
            foreach ( Player p in this.Players )
            {
                p.RationedExperience = ( double )p.Experience / maxExp;
            }

            if ( this.PlayersLoaded != null )
            {
                this.PlayersLoaded( this.Players );
            }
        }
    }
}
